#ifndef _FFT_TEST_
#define _FFT_TEST_

#include <iomanip>
#include <iostream>
#include <list>
using namespace std;

#define DEBUG 0
#define DATA_EXTENTION_SHIFT 3

#define ERROR_THRESHOLD_V (0.000001)
#define ERROR_THRESHOLD_P (0.001)


enum FFT_type {COMPLEX_FFT=1, COMPLEX_IFFT, REAL_FFT, REAL_IFFT};


class DataDesc {
private:
	unsigned int fft_data_size;
	unsigned int fft_row;
public:
	int projection;
	int width;
	int height;

	DataDesc(int pro, int h, int w) {
		projection = pro;
		width = w;
		height = h;
		fft_row = ((unsigned int)w)<<DATA_EXTENTION_SHIFT;
		fft_data_size = (unsigned int)(pro*h*fft_row);
	}

	unsigned int get_fft_size() { return fft_data_size; }
	unsigned int get_fft_row() { return fft_row; }
};

class CPU_FFT_Manager {
private:
	unsigned int data_size;
	DataDesc *data_desc;
	float *result_data;
public:
	~CPU_FFT_Manager() {
		free(result_data);
	}
	CPU_FFT_Manager(DataDesc *dd) {
		this->data_size = dd->get_fft_size();
		this->data_desc = dd;
		unsigned int result_size = (unsigned int)dd->projection*dd->height*dd->width;
		this->result_data = (float *)malloc(result_size*sizeof(float));
		memset(result_data, 0, result_size*sizeof(float));
	}
	void cpu_run(float *input_data);
	float *get_result() { return this->result_data; }
};

inline void init_data(float *data_raw, unsigned int length) {
	for(unsigned int i=0; i<length; i++) {
		data_raw[i] = (float)(i+1);
	}
}

float gpgpu_fft(FFT_type type);
void gpu_copy_input_data(float *input_data, unsigned int data_width, unsigned int data_height);
void gpu_copy_back(float *output_data);

float *cpu_fft( float *input_data, unsigned int data_width, unsigned int data_height, FFT_type type );

void compare_by_percent(float *dataset1, float *dataset2, unsigned int length, unsigned int raw=0);
void compare_by_value(float *dataset1, float *dataset2, unsigned int length, unsigned int raw=0);

class Error_Msg {
	int raw_index;
	int col_index;
	float gpgpu_value;
	float cpu_value;
	float error_value;
public:
	Error_Msg(int raw, int col, float g_value, float c_value, float error) {
       raw_index = raw;
	   col_index = col;
	   gpgpu_value = g_value;
	   cpu_value = c_value;
	   error_value = error;
	}

	void print_p() {
		cout<<"error data at ["<<raw_index<<","<<col_index<<"] ("<<setprecision(10)<<gpgpu_value<<","<<cpu_value<<")"<<"error per "<<setprecision(10)<<error_value<<endl;
	}

	void print_v() {
		cout<<"error data at ["<<raw_index<<","<<col_index<<"] error diff "<<setprecision(9)<<error_value<<endl;
	}
};

extern list<Error_Msg> error_messages_p;
extern list<Error_Msg> error_messages_v;

inline void dump_error() {
  cout << "percent error messges list: "<<endl;
  list<Error_Msg>::iterator p = error_messages_p.begin();
  while(p != error_messages_p.end()) {
    p->print_p();
    p++;
  }
  cout << endl; 
 
  /*
  cout << "value error messges list: "<<endl;
  p = error_messages_v.begin();
  while(p != error_messages_v.end()) {
    p->print_v();
    p++;
  }
  cout << endl;
  */
}

#define DUMP_NUM 20
inline void dump_result(float *result) {

	for(int i=0;i<DUMP_NUM;i+=2) {
		cout<<result[i]<<","<<result[i+1]<<endl;
	}
	
}

#define DUMP_INT_NUM ((1984<<2)+4)
typedef union {
	float fv;
	unsigned int uv;
	int iv;
} number;

inline void dump_result_int(float *result) {
	for(int i=0;i<DUMP_NUM;i+=2) {
		//cout<<hex <<result[i]<<","<<result[i+1]<<endl;
		number num1;
		num1.fv = result[i];
		number num2;
		num2.fv = result[i+1];
		printf("%u,%u\n",num1.uv,num2.uv);
	}
}

inline void dump_cplx_int(float *data, int project, int height, int line_num, int line_width, int dump_num) {
	   if(!DEBUG)
			return;
		int base = ((project*height)+line_num)*line_width;
		int index = 0;
		for(int k=0;k<dump_num*2;k+=2) {
			index = base + k;
			number num1;
			num1.fv = data[index];
			number num2;
			num2.fv = data[index+1];
			cout << hex <<"("<< num1.iv <<","<< num2.iv <<") ";
		}
		cout<<endl;
}

inline void dump_cplx(float *data, int project, int height, int line_num, int line_width, int dump_num) {
	    if(!DEBUG)
			return;
		int base = ((project*height)+line_num)*line_width;
		int index = 0;
		for(int k=0;k<dump_num*2;k+=2) {
			index = base + k;
			cout<<"("<< data[index] <<","<< data[index+1] <<") ";
		}
		cout<<endl;
}

inline void dump_real(float *data, int project, int height, int line_num, int line_width, int dump_num) {
		if(!DEBUG)
			return;
		int base = ((project*height)+line_num)*line_width;
		int index = 0;
		for(int k=0;k<dump_num;k++) {
			index = base + k;
			cout<<data[index]<<", ";
		}
		cout<<endl;
}

#endif
